package summer.codegenerator.common.util;

import java.io.File;
import java.io.FileFilter;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.net.JarURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Objects;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

import org.apache.commons.lang3.StringUtils;

import summer.codegenerator.model.base.EnumIndexable;

/**
 * <a href="https://my.oschina.net/huangyong/blog/158360">引用</a>
 * 
 * @author 蒋振
 * @creation 2019年6月14日 下午5:43:41
 */
public class ClazzUtil {
	
	/**
	 * 获取泛型类类对象的第一个泛型参数
	 *
	 * 
	 * @param claszz
	 * @return
	 * @author: jiangz
	 * @date: Created on 2019年7月24日 下午1:49:38
	 */
	public static Class<?> getFirstParameterizedClass(Class<?> claszz) {
		Type type = claszz.getClass().getGenericSuperclass();
		if (type instanceof ParameterizedType) {
			ParameterizedType pt = (ParameterizedType) type;
			Type[] params = pt.getActualTypeArguments();
			if(params != null && params[0] != null) {
				return (Class<?>) params[0];
			}
		}
		return null;
	}
	
	/**
	 * 获取泛型类类对象的第一个泛型参数
	 *
	 * 
	 * @param claszz
	 * @return
	 * @author: jiangz
	 * @date: Created on 2019年7月24日 下午1:49:38
	 */
	public static Type getMethodReturnFirstParameterizedClass(Method method) {
		ParameterizedType returnGenericType = (ParameterizedType)method.getGenericReturnType();
		Type[] listActualTypeArguments = returnGenericType.getActualTypeArguments();
		if (listActualTypeArguments != null && listActualTypeArguments.length > 0) {
			return listActualTypeArguments[0];
		}
		return null;
	}

	/**
	 * 通过枚举类类对象和枚举索引值获得枚举对象
	 *
	 * 
	 * @param clazz
	 * @param enumIndex
	 * @return
	 * @author: jiangz
	 * @date: Created on 2019年7月19日 下午4:27:59
	 */
	public static Object getEnumObjByClassObjAndEnumIndex(Class<?> clazz, Integer enumIndex) {
		if (clazz.isEnum() && EnumIndexable.class.isAssignableFrom(clazz)) {
			Object[] enumConstants = clazz.getEnumConstants();
			for (Object enumConstant : enumConstants) {
				EnumIndexable ei = (EnumIndexable) enumConstant;
				if (Objects.equals(ei.getIndex(), enumIndex)) {
					return enumConstant;
				}
			}
		}
		return null;
	}

	/**
	 * 获取指定包名下的所有类
	 * @param packageName       包名称
	 * @param isRecursive       是否查询所有子包
	 * @return
	 */
	public static List<Class<?>> getClassList(String packageName, boolean isRecursive) {
		List<Class<?>> classList = new ArrayList<Class<?>>();
		try {
			Enumeration<URL> urls = Thread.currentThread().getContextClassLoader()
					.getResources(packageName.replaceAll("\\.", "/"));
			while (urls.hasMoreElements()) {
				URL url = urls.nextElement();
				if (url != null) {
					String protocol = url.getProtocol();
					if (protocol.equals("file")) {
						// class
						String packagePath = url.getPath();
						addClass(classList, packagePath, packageName, isRecursive);
					} else if (protocol.equals("jar")) {
						// jar
						JarURLConnection jarURLConnection = (JarURLConnection) url.openConnection();
						JarFile jarFile = jarURLConnection.getJarFile();
						Enumeration<JarEntry> jarEntries = jarFile.entries();
						while (jarEntries.hasMoreElements()) {
							JarEntry jarEntry = jarEntries.nextElement();
							String jarEntryName = jarEntry.getName();
							if (jarEntryName.endsWith(".class")) {
								String className = jarEntryName.substring(0, jarEntryName.lastIndexOf(".")).replaceAll(
										"/", ".");
								if (isRecursive
										|| className.substring(0, className.lastIndexOf(".")).equals(packageName)) {
									classList.add(Class.forName(className));
								}
							}
						}
					}
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return classList;
	}

	/**
	 * 获取指定包名下的所有类（可根据注解进行过滤）
	 * @param packageName       包名称
	 * @param annotationClass   注解名称
	 * @return
	 */
	public static List<Class<?>> getClassListByAnnotation(String packageName,
			Class<? extends Annotation> annotationClass) {
		List<Class<?>> classList = new ArrayList<Class<?>>();
		try {
			Enumeration<URL> urls = Thread.currentThread().getContextClassLoader()
					.getResources(packageName.replaceAll("\\.", "/"));
			while (urls.hasMoreElements()) {
				URL url = urls.nextElement();
				if (url != null) {
					String protocol = url.getProtocol();
					if (protocol.equals("file")) {
						// class
						String packagePath = url.getPath();
						addClassByAnnotation(classList, packagePath, packageName, annotationClass);
					} else if (protocol.equals("jar")) {
						// jar
						JarURLConnection jarURLConnection = (JarURLConnection) url.openConnection();
						JarFile jarFile = jarURLConnection.getJarFile();
						Enumeration<JarEntry> jarEntries = jarFile.entries();
						while (jarEntries.hasMoreElements()) {
							JarEntry jarEntry = jarEntries.nextElement();
							String jarEntryName = jarEntry.getName();
							if (jarEntryName.endsWith(".class")) {
								String className = jarEntryName.substring(0, jarEntryName.lastIndexOf(".")).replaceAll(
										"/", ".");
								Class<?> cls = Class.forName(className);
								if (cls.isAnnotationPresent(annotationClass)) {
									classList.add(cls);
								}
							}
						}
					}
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return classList;
	}

	private static void addClass(List<Class<?>> classList, String packagePath, String packageName, boolean isRecursive) {
		try {
			File[] files = getClassFiles(packagePath);
			if (files != null) {
				for (File file : files) {
					String fileName = file.getName();
					if (file.isFile()) {
						String className = getClassName(packageName, fileName);
						classList.add(Class.forName(className));
					} else {
						if (isRecursive) {
							String subPackagePath = getSubPackagePath(packagePath, fileName);
							String subPackageName = getSubPackageName(packageName, fileName);
							addClass(classList, subPackagePath, subPackageName, isRecursive);
						}
					}
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	private static File[] getClassFiles(String packagePath) {
		return new File(packagePath).listFiles(new FileFilter() {

			@Override
			public boolean accept(File file) {
				return (file.isFile() && file.getName().endsWith(".class")) || file.isDirectory();
			}
		});
	}

	private static String getClassName(String packageName, String fileName) {
		String className = fileName.substring(0, fileName.lastIndexOf("."));
		if (StringUtils.isNotEmpty(packageName)) {
			className = packageName + "." + className;
		}
		return className;
	}

	private static String getSubPackagePath(String packagePath, String filePath) {
		String subPackagePath = filePath;
		if (StringUtils.isNotEmpty(packagePath)) {
			subPackagePath = packagePath + "/" + subPackagePath;
		}
		return subPackagePath;
	}

	private static String getSubPackageName(String packageName, String filePath) {
		String subPackageName = filePath;
		if (StringUtils.isNotEmpty(packageName)) {
			subPackageName = packageName + "." + subPackageName;
		}
		return subPackageName;
	}

	private static void addClassByAnnotation(List<Class<?>> classList, String packagePath, String packageName,
			Class<? extends Annotation> annotationClass) {
		try {
			File[] files = getClassFiles(packagePath);
			if (files != null) {
				for (File file : files) {
					String fileName = file.getName();
					if (file.isFile()) {
						String className = getClassName(packageName, fileName);
						Class<?> cls = Class.forName(className);
						if (cls.isAnnotationPresent(annotationClass)) {
							classList.add(cls);
						}
					} else {
						String subPackagePath = getSubPackagePath(packagePath, fileName);
						String subPackageName = getSubPackageName(packageName, fileName);
						addClassByAnnotation(classList, subPackagePath, subPackageName, annotationClass);
					}
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}